// 文件分片
// -1 是完成的文件名 
// [0..n] 是分片文件名
// lock 是锁文件名,文件大小==0说明无锁,用于锁目录操作 , 若存在锁则直接返回Err,而非等待解锁 //可能交给业务去实现锁,而非文件锁
// 信息(len = 5(字节)) + 原始文件名 + 额外信息 + id  = 目录名 // 全是字符(utf8) //id一般是md5,长度无所谓
// 信息: [0,2) 是[原始文件名]的长度 ,分配3字符
// 		[2,4) 是[额外信息] 的长度 , 分配3字符
//		[4,5) 是[额外信息] 的类型 , 分配1字符 //也可以说是 目录类型,不同的业务若怕重复可以换不同类型
// 需要注意的是,系统的实际长度是有限制的
//<< 调整,不使用字节,而是使用字符 ,妈的字节转uft8一直有问题,很难存储
//# 为什么要把额外信息存在目录里呢,存在文件里,不好吗
//# 存在目录里,可以形成独立id,也可以快速找到对应目录,若是文件里,那么有100个目录,就要读100文件...

use std::{fs::{File, DirBuilder, self}, path::{Path, PathBuf}, io::{Write, Read}};
use crate::tool_bag::FREE_CONFIG;
use thiserror::Error;

/// 关于分片目录的操作
pub trait Dir {
	fn get_name_len(&self)->usize;

	fn get_message_len(&self)->usize;
	
	fn set_message_type(&mut self,s:impl Into<char>);
	fn get_message_type(&self)->char;
	
	fn set_name(&mut self,s:impl Into<String>);
	fn get_name(&self)->&String;
	
	fn set_message(&mut self,s:impl Into<String>);
	fn get_message(&self)->&String;
	
	fn set_id(&mut self,s:impl Into<String>);
	fn get_id(&self)->&String;
	
	fn set(s:impl Into<String>) -> Result<Self,String> 
		where Self: std::marker::Sized;
	fn get(&self)->String;
	//<< &str 转 Dir 使用From<T> 实现
}
#[derive(Debug,Clone,Default,PartialEq,Eq)]
pub struct PieceDir { // 全力逃离生命周期~ 呜呜呜
	// name_len:u16,
	// message_len:u16,
	message_type:char,
	name:String,
	message:String,
	id:String
}

impl Dir for PieceDir {

    fn get_name_len(&self)->usize {
        self.name.chars().count()
    }
    fn get_message_len(&self)->usize {
        self.message.chars().count()
    }

	fn set_message_type(&mut self,s:impl Into<char>) {
        self.message_type = s.into();
    }
    fn get_message_type(&self)->char {
        self.message_type
    }

	fn set_name(&mut self,s:impl Into<String>) {
        self.name = s.into();
    }
    fn get_name(&self)->&String {
        &self.name
    }

	fn set_message(&mut self,s:impl Into<String>) {
        self.message = s.into();
    }
    fn get_message(&self)->&String {
        &self.message
    }

	fn set_id(&mut self,s:impl Into<String>) {
        self.id = s.into();
    }
    fn get_id(&self)->&String {
        &self.id
    }

    fn set(s:impl Into<String>) -> Result<Self,String> {
        let s:String = s.into();
		let s = s.chars().into_iter().collect::<Vec<char>>();
		if s.len() < 7 {
			return Err("s长度不满足最低要求,需要最低7个字符".to_string());
		}
		let name_len = &s[0..3].iter().collect::<String>().parse::<usize>();
		let message_len =  &s[3..6].iter().collect::<String>().parse::<usize>();
		let message_type =  s[6];
		let (name_len,message_len,message_type) = match (name_len,message_len,message_type){//v3兄弟有灾同当
			(Ok(v1),Ok(v2),v3) => (v1,v2,v3) ,
			_ => return Err("字符格式不满足,前6个字符需要是[0-9]".to_string())
		};
		if s.len() < 7+name_len+message_len {
			return Err(format!("s长度不满足格式要求,需要最低个{}字符",7+name_len+message_len));
		}
		let name = s[7..(7+name_len)].iter().collect::<String>();
		let message = s[(7+name_len)..((7+name_len)+message_len)].iter().collect::<String>();
		let id = s[((7+name_len)+message_len..)].iter().collect::<String>();

		Ok(PieceDir {
			message_type,
			name,
			message,
			id
		})
    }

    fn get(&self)->String {
		format!("{:0>3}{:0>3}{}{}{}{}"
			,self.get_name_len()
			,self.get_message_len()
			,self.get_message_type()
			,self.get_name()
			,self.get_message()
			,self.get_id())
    }
}


//<< 关于额外信息类型,未来会发送大改,未来会使用 接口 来代替
// [额外信息类型]
pub enum ExtraMassage {
	None ,// 0x00 没有额外信息
	Some ,// 0x01 有额外信息,但是我不关心是啥
	// Size ,// 0x02 额外信息是 分片大小
	Other(char)
}
impl From<ExtraMassage> for char {
    fn from(a: ExtraMassage) -> Self {
        match a {
			ExtraMassage::None => 0x00 as char,
			ExtraMassage::Some => 0x01 as char ,
			ExtraMassage::Other(v) => v
		}
    }
}
impl From<char> for ExtraMassage {
    fn from(a: char) -> Self {
		match a as i32{
			0x00 => ExtraMassage::None,
			0x01 => ExtraMassage::Some,
			_ => ExtraMassage::Other(a)
		}
    }
}

/// 0x02 额外信息是 分片大小,表示内部分片固定大小
/// .0 : 单位字节
pub struct Size(pub usize);
impl Size {
	
}
impl From<usize> for Size {
    fn from(a: usize) -> Self {
        Self(a)
    }
}
impl From<Size> for char {
    fn from(_: Size) -> Self {
        0x02 as char
    }
}


#[derive(Error,Debug)]
pub enum PieceError {
	#[error("分片创建失败[{0}]")]
	CreatePieceFail(usize),
	#[error("找不到分片[{0}]")]
	NotFindPiece(isize)
}


pub trait Piece {
	/// data:数据推入分片缓存
	/// -> 当期分片的大小(字节)
	fn add(&mut self,data:&[u8])->usize;
	/// 推入文件
	fn push(&mut self)->Result<(),PieceError>;
	/// 抓一个分片
	fn fetch(&mut self,i:isize) -> Result<File,PieceError>;
	/// 当前目录中以有分片数
	fn piece_num(&self)->usize;
}
pub trait PieceManage : Piece{
	/// new 即创建目录 , 目录存在则说明是续传
	fn new(dir:PieceDir)->Self;
	/// 合成文件 
	fn make_file(&self)->Result<File,PieceError>;
	/// 清除目录下所有文件,包括目录
	fn clear(self);
}
#[derive(Debug,Clone)]
pub struct PieceManager {
	path_dir:PathBuf,
	piece_num:usize,
	data:Vec<u8>
}
impl Piece for PieceManager {
    fn add(&mut self,data:&[u8])->usize {
		self.data.append(&mut data.to_vec());
        self.data.len()
    }

    fn push(&mut self)->Result<(),PieceError> {
        let file_name = self.piece_num().to_string();
		let mut piece_file = match File::create(self.path_dir.join(file_name)) {
				Ok(v) => v ,
				Err(e) => {
					return Err(PieceError::CreatePieceFail(self.piece_num()));
				} 
			};
		
		piece_file.write(self.data.as_ref());
		self.data = Vec::new();
		self.piece_num +=1;
		Ok(())
    }

    fn fetch(&mut self,i:isize) -> Result<File,PieceError> {
        match File::open(self.path_dir.join(i.to_string())) {
			Ok(v) => Ok(v) ,
			Err(e) => {
				Err(PieceError::NotFindPiece(i))
			}
		}
    }

    fn piece_num(&self)->usize {
        self.piece_num
    }
}
impl PieceManage for PieceManager {
    fn new(dir:PieceDir)->Self {
		let config = &FREE_CONFIG.path.cent_piece;
		let path = Path::new(config).join(dir.get());
		let piece_num = match path.is_dir() {
			true => { //存在目录 是续传
				let list_dir = std::fs::read_dir(path.as_path()).unwrap();//目录下的文件
				let mut vec = Vec::<usize>::new();
				for v in list_dir {
					if let Ok(v) = v {
						let file_name = v.file_name().into_string().unwrap();
						if let Ok(v) = file_name.parse::<usize>(){
							vec.push(v);
						}
					} 
				}
				// vec.sort();//不应该出现 [0..n] 中缺少数字,也就是不能出现 [0,1,3] 缺少2
				vec.len()
			}
			false => {
				DirBuilder::new().create(path.as_path());
				0
			}
		};
        PieceManager { path_dir: path, piece_num: piece_num, data: Vec::new() }
    }

    fn make_file(&self)->Result<File,PieceError> {
		let mut file = File::create(self.path_dir.join((-1).to_string())).unwrap();
		for v in 0..self.piece_num() {
			let piece_name = v.to_string();
			let mut piece = match File::open(self.path_dir.join(piece_name)) {
				Ok(v) => v ,
				Err(e) => return Err(PieceError::NotFindPiece(v as isize))
			};
			let mut buf = [0_u8;1024];
			while let Ok(v) = piece.read(&mut buf){
				if v==0 {break;}
				(&mut file).write(&buf[0..v]);
			};
		}
		Ok(file)
    }

    fn clear(self) {
        fs::remove_dir_all(self.path_dir);
    }
}




#[cfg(test)]
mod tests{
    use std::io::Read;

    use crate::tool_bag::file_piece::ExtraMassage;

    use super::{PieceDir, Dir, PieceManager, PieceManage, Piece};

	#[test]
	fn PieceDir_create(){
		let a = PieceDir::set("006004\u{1}菜虚坤.篮球鸡尼太美123456789").unwrap();
		println!("{:?}",a);
		let mut b = PieceDir::default();
		b.set_name("菜虚坤.篮球");
		b.set_message("鸡尼太美");
		b.set_id("123456789");
		b.set_message_type(ExtraMassage::Some);
		println!("{:?}",b);
		assert_eq!(a,b);
		println!("{}",a.get());
		println!("{}",b.get());
	}

	fn PieceManager_new() -> PieceManager{
		PieceManager::new(PieceDir::set("006004\u{1}菜虚坤.篮球鸡尼太美123456789").unwrap())
	} 
	#[test]
	fn PieceManager_create() {
		let a = PieceDir::set("006004\u{1}菜虚坤.篮球鸡尼太美123456789").unwrap();
		println!("{:?}",a.get());
		let b = PieceManager::new(a);
		println!("{:#?}",b);
	}
	
	#[test]
	fn PieceManager_push_datas(){
		let mut b = PieceManager_new();
		let files = ["0123456789".as_bytes(),"abcdefghil".as_bytes()];
		for v in 0..10 {
			b.add(files.get(v%2).unwrap());
			b.push();
		}
	}

	#[test]
	fn PieceManager_make_file(){
		match PieceManager_new().make_file() {
			Err(e) =>{
				println!("{:#?}",e.to_string());
			},
			_ => ()
		}
	}
	#[test]
	fn PieceManager_clear(){
		PieceManager_new().clear();
	}
	#[test]
	fn PieceManager_fetch(){
		let mut file = PieceManager_new().fetch(10).unwrap();
		let mut s = String::new();
		file.read_to_string(&mut s);
		println!("{}",s);
	}
	#[test]
	fn f(){
		
	}
}